package org.dxy.domain.model.aggregate.impl;

import lombok.Builder;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.dxy.domain.command.workstation.*;
import org.dxy.domain.event.DomainEvent;
import org.dxy.domain.event.workstation.*;
import org.dxy.domain.model.aggregate.Workstation;
import org.dxy.domain.model.valobj.WorkOrderId;
import org.dxy.domain.model.valobj.WorkstationId;
import org.dxy.types.enums.WorkstationStatusEnum;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Collectors;
import java.util.Objects;

/**
 * @description: 工位聚合根实现类
 * @author: dxy 
 * @createTime: 2025/6/22 17:26 
 */
@Slf4j
@Getter
@Builder
public class WorkstationAggRoot implements Workstation {

    private WorkstationId workstationId;               // 工位ID（聚合唯一标识）
    private WorkOrderId workOrderId;
    private Long processStepId;
    private WorkstationStatusEnum status;              // 工位状态
    private Long productionLineId;                     // 所属产线ID
    private String equipmentIds;                       // 关联设备ID列表（逗号分隔）
    private LocalDateTime lastMaintenanceTime;         // 上次维护时间
    private LocalDateTime nextMaintenanceTime;         // 下次维护时间
    private Long currentStepId;                        // 当前执行的工艺步骤ID

    // === 事件集合 ===
    private final List<DomainEvent> domainEvents = new ArrayList<>();

    // === 事件相关方法 ===
    @Override
    public List<DomainEvent> getDomainEvents() {
        return Collections.unmodifiableList(domainEvents); // 返回一个 不可修改的视图
    }

    @Override
    public void addDomainEvent(DomainEvent event) {
        domainEvents.add(event);
    }

    @Override
    public void clearDomainEvents() {
        domainEvents.clear();
    }

    @Override
    public boolean hasDomainEvents() {
        return !domainEvents.isEmpty();
    }

    @Override
    public boolean hasNoDomainEvents() {
        return domainEvents.isEmpty();
    }


    public static Workstation create(CreateWorkstationCommand command, Long workstationId) {
        return WorkstationAggRoot.builder()
                .workstationId(WorkstationId.of(workstationId))
                .workOrderId(WorkOrderId.of(command.getWorkOrderId()))        // 所属工单ID
                .processStepId(command.getProcessStepId())    // 工艺步骤ID
                .status(WorkstationStatusEnum.IDLE)
                .build();
    }


    @Override
    public void stationIn(WorkstationInCommand command) {
        log.info("执行工位进站: command={}", command);

        // 业务规则验证
        if (!"CREATED".equals(this.status)) {
            throw new IllegalStateException("当前状态不允许进站操作: " + this.status);
        }

        // 更新状态
        this.status = WorkstationStatusEnum.RUNNING;

        // 发布进站事件
        WorkstationInEvent event = WorkstationInEvent.builder()
                .workstationId(command.workstationId())
                .build();

        addDomainEvent(event);

        log.info("工位进站完成: command:{}", command);
    }

    @Override
    public void stationOut(WorkstationOutCommand command) {
        log.info("执行工位出站: workstationId={}, serialNumber={}, qualityResult={}",
                command.workstationId(), command.serialNumber(), command.qualityResult());

        // 业务规则验证
        if (!"IN".equals(this.status)) {
            throw new IllegalStateException("当前状态不允许出站操作: " + this.status);
        }

        // 更新状态
        this.status = WorkstationStatusEnum.IDLE;

        // 发布出站事件
        WorkstationOutEvent event = WorkstationOutEvent.builder()
                .workstationId(command.workstationId())
                .workOrderCode(command.workOrderCode())
                .serialNumber(command.serialNumber())
                .qualityResult(command.qualityResult())
                .build();

        addDomainEvent(event);

        log.info("工位出站完成: workstationId={}, serialNumber={}, qualityResult={}",
                command.workstationId(), command.serialNumber(), command.qualityResult());
    }




    @Override
    public WorkstationPausedEvent pause(String reason, String currentOperator) {
        if (this.status != WorkstationStatusEnum.RUNNING) {
            throw new IllegalStateException("仅允许在运行中状态暂停工位");
        }

        this.status = WorkstationStatusEnum.PAUSED;

        return WorkstationPausedEvent.builder()
                .workstationId(this.workstationId.getValue())
                .reason(reason)
                .operator(currentOperator)
                .pausedAt(LocalDateTime.now())
                .build();
    }

    @Override
    public WorkstationResumedEvent resume(ResumeWorkstationCommand command) {
        if (this.status != WorkstationStatusEnum.PAUSED) {
            throw new IllegalStateException("只有暂停状态的工位才能恢复");
        }

        this.status = WorkstationStatusEnum.RUNNING;

        return WorkstationResumedEvent.builder()
                .workstationId(this.workstationId.getValue())
                .resumedAt(LocalDateTime.now())
                .build();
    }

    @Override
    public WorkstationErrorOccurredEvent reportError(ReportWorkstationErrorCommand command) {
        // 不限制原始状态，允许任意状态上报错误
        this.status = WorkstationStatusEnum.ERROR;

        return WorkstationErrorOccurredEvent.builder()
                .workstationId(this.workstationId.getValue())
                .errorType(command.getErrorType())
                .errorMessage(command.getErrorMessage())
                .occurredAt(LocalDateTime.now())
                .build();
    }

    @Override
    public WorkstationResetEvent reset(ResetWorkstationCommand command) {
        if (this.status != WorkstationStatusEnum.ERROR) {
            throw new IllegalStateException("仅异常状态下才能执行重置操作");
        }

        this.status = WorkstationStatusEnum.IDLE;

        return WorkstationResetEvent.builder()
                .workstationId(this.workstationId.getValue())
                .resetAt(LocalDateTime.now())
                .build();
    }

    @Override
    public WorkstationAssignedDevicesEvent assignDevices(AssignDevicesToWorkstationCommand command) {
        List<Long> deviceIds = command.getDeviceIds();
        this.equipmentIds = deviceIds.stream()
                .map(String::valueOf)
                .reduce((a, b) -> a + "," + b)
                .orElse("");

        return WorkstationAssignedDevicesEvent.builder()
                .workstationId(this.workstationId.getValue())
                .assignedDeviceIds(deviceIds)
                .build();
    }

    @Override
    public WorkstationStoppedEvent stop(StopWorkstationCommand command) {
        if (this.status == WorkstationStatusEnum.PAUSED) {
            throw new IllegalStateException("工位已停止或已完成，不能再次停止");
        }

        this.status = WorkstationStatusEnum.PAUSED;

        return WorkstationStoppedEvent.builder()
                .workstationId(this.workstationId.getValue())
                .reason(command.getReason())
                .stoppedAt(LocalDateTime.now())
                .build();
    }

    @Override
    public WorkstationRemovedEvent remove(RemoveWorkstationCommand command) {
        if (this.status == WorkstationStatusEnum.PAUSED) {
            // 可以删除
            this.status = WorkstationStatusEnum.REMOVED;
        } else {
            throw new IllegalStateException("仅允许对已停止或已完成的工位进行删除操作");
        }

        return WorkstationRemovedEvent.builder()
                .workstationId(this.workstationId.getValue())
                .removedAt(LocalDateTime.now())
                .build();
    }

    @Override
    public WorkstationDevicesAppendedEvent appendDevices(AppendDevicesToWorkstationCommand command) {
        if (this.equipmentIds == null) {
            this.equipmentIds = "";
        }

        List<Long> newDevices = command.getDeviceIds().stream()
                .filter(id -> !this.equipmentIds.contains(id.toString()))
                .toList();

        if (!newDevices.isEmpty()) {
            String newDeviceIds = newDevices.stream()
                    .map(String::valueOf)
                    .reduce((a, b) -> a + "," + b)
                    .orElse("");
            
            this.equipmentIds = this.equipmentIds.isEmpty() ? newDeviceIds : this.equipmentIds + "," + newDeviceIds;
        }

        return WorkstationDevicesAppendedEvent.builder()
                .workstationId(this.workstationId.getValue())
                .appendedDeviceIds(newDevices)
                .allDeviceIds(parseEquipmentIds(this.equipmentIds))
                .build();
    }

    @Override
    public WorkstationDevicesRemovedEvent removeDevices(List<Long> deviceIds, String operator) {
        if (this.equipmentIds != null && !this.equipmentIds.isEmpty()) {
            String[] equipmentIdArray = this.equipmentIds.split(",");
            StringBuilder newEquipmentIds = new StringBuilder();
            
            for (String equipmentId : equipmentIdArray) {
                if (!deviceIds.contains(Long.valueOf(equipmentId.trim()))) {
                    if (newEquipmentIds.length() > 0) {
                        newEquipmentIds.append(",");
                    }
                    newEquipmentIds.append(equipmentId.trim());
                }
            }
            
            this.equipmentIds = newEquipmentIds.toString();
        }

        return WorkstationDevicesRemovedEvent.builder()
                .workstationId(this.workstationId.getValue())
                .removedDeviceIds(deviceIds)
                .operator(operator)
                .occurredAt(LocalDateTime.now())
                .build();
    }

    @Override
    public WorkstationDevicesReleasedEvent releaseAllDevices(String operator) {
        this.equipmentIds = "";

        return WorkstationDevicesReleasedEvent.builder()
                .workstationId(this.workstationId.getValue())
                .operator(operator)
                .occurredAt(LocalDateTime.now())
                .build();
    }

    private List<Long> parseEquipmentIds(String equipmentIds) {
        if (equipmentIds == null || equipmentIds.isEmpty()) {
            return new ArrayList<>();
        }
        return Arrays.stream(equipmentIds.split(","))
                .map(String::trim)
                .map(Long::valueOf)
                .collect(Collectors.toList());
    }
}
